package com.mdd.front.service.coupon.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.query.MPJQueryWrapper;
import com.mdd.common.config.GlobalConfig;
import com.mdd.common.core.PageResult;
import com.mdd.common.entity.system.Coupon;
import com.mdd.common.entity.system.CouponReceive;
import com.mdd.common.enums.CouponEnum;
import com.mdd.common.mapper.course.CourseMapper;
import com.mdd.common.mapper.order.OrderCourseMapper;
import com.mdd.common.mapper.system.CouponMapper;
import com.mdd.common.mapper.system.CouponReceiveMapper;
import com.mdd.common.utils.RedisUtil;
import com.mdd.common.utils.TimeUtil;
import com.mdd.common.vo.system.ConponVo;
import com.mdd.front.LikeFrontThreadLocal;
import com.mdd.front.service.coupon.ICouponService;
import com.mdd.front.validate.CouponReceiveParam;
import com.mdd.front.validate.CouponUseParam;
import com.mdd.front.validate.PageParam;
import com.mdd.front.vo.coupon.CouponCenterVo;
import com.mdd.front.vo.coupon.CouponMineVo;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.annotation.Resource;
import java.text.SimpleDateFormat;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.ZoneOffset;
import java.util.*;

@Service
public class CouponServiceImpl implements ICouponService {

    @Resource
    private CouponMapper couponMapper;

    @Resource
    private CouponReceiveMapper couponReceiveMapper;

    @Resource
    private CourseMapper courseMapper;

    @Resource
    private OrderCourseMapper orderCourseMapper;

    private final SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");

    @Override
    public List<CouponCenterVo> center() {
        Integer userId = LikeFrontThreadLocal.getUserId();
        List<Coupon> coupons = this.couponMapper.getByStatusAndReceivingMethod(CouponEnum.STATUS_GOING.getCode(), CouponEnum.RECEIVE_METHOD_USER.getCode());
        List<CouponCenterVo> centerVos = new LinkedList<>();
        coupons.forEach(item-> {
            CouponCenterVo centerVo = new CouponCenterVo();

            centerVo.setId(item.getId());
            centerVo.setReceiveStatus(checkCoupon(item, userId));

            Integer validType = item.getValidType();
            Integer validDays = item.getValidDays();
            String validTime = "";
            if (validType != null && validType.intValue() == 2) {
                validTime = "领券当日起" + validDays + "天内可用";
            } else if (validType != null && validType.intValue() == 3) {
                validTime = "领券次日起" + validDays + "天内可用";
            } else {
                validTime = TimeUtil.timestampToDate(item.getValidStartTime(), "yyyy.MM.dd HH:mm") + "~" + TimeUtil.timestampToDate(item.getValidEndTime(), "yyyy.MM.dd HH:mm");
            }

            centerVo.setTitle(item.getTitle());
            centerVo.setAmount(item.getAmount());
            centerVo.setFullAmount(item.getFullAmount());
            centerVo.setValidTime(validTime);
            centerVo.setUsed(item.getUsed());
            centerVo.setType(item.getType());
            centerVo.setCourses(courseMapper.getCourseByCouponId(item.getId()));
            centerVos.add(centerVo);
        });
        return centerVos;
    }


    private int checkCoupon(Coupon item, Integer userId) {
        Integer dailyTakeCount = (Integer) RedisUtil.get("coupon_daily_take_count_" + format.format(new Date()));
        //每天已经领取的优惠券数量
        dailyTakeCount = dailyTakeCount != null ? dailyTakeCount : 0;
        Short isQuota = item.getIsQuota();
        isQuota = isQuota != null ? isQuota : 0;
        Integer quota = item.getQuota();
        quota = quota != null ? quota : 0;
        Integer takeType = item.getTakeType();
        Integer takeQuota = item.getTakeQuota();

        if (isQuota.intValue() != 1 && quota.intValue() <= dailyTakeCount.intValue()) {
            //已抢光
            return 3;
        } else {
            if (takeType != null && takeType.intValue() == 2) {
                //每个用户只能领几张
                Integer count = couponReceiveMapper.countByUserIdAndCouponId(userId, item.getId());
                if (count.intValue() >= takeQuota.intValue()) {
                    //已领取
                    return 2;
                } else {
                    //可领取
                    return 1;
                }
            } else if (takeType != null && takeType.intValue() == 3) {
                //每个用户每天只能领几张
                Long startTime = LocalDateTime.of(LocalDate.now(), LocalTime.MIN).toInstant(ZoneOffset.of("+8")).toEpochMilli() / 1000;
                Long endTime = LocalDateTime.of(LocalDate.now(), LocalTime.of(23,59,59)).toInstant(ZoneOffset.of("+8")).toEpochMilli() / 1000;
                Integer count = couponReceiveMapper.countDaliyByUserIdAndCouponId(userId, item.getId(), startTime, endTime);
                if (count.intValue() >= takeQuota.intValue()) {
                    //已领取
                    return 2;
                } else {
                    //可领取
                    return 1;
                }
            } else {
                //可领取
                return 1;
            }
        }
    }

    @Override
    public void receive(CouponReceiveParam couponReceiveParam) {
        Long curTimestamp = TimeUtil.timestamp();
        Integer userId = LikeFrontThreadLocal.getUserId();
        if( couponReceiveParam != null) {
            Coupon model = couponMapper.selectOne(
                    new QueryWrapper<Coupon>()
                            .eq("id", couponReceiveParam.getCouponId())
                            .last("limit 1"));

            Assert.isTrue(model.getStatus() != null && model.getStatus().intValue() == CouponEnum.STATUS_GOING.getCode(), "优惠券不可用");
            Assert.isTrue(checkCoupon(model, userId) == 1, "优惠券不可领取");

            if (model != null) {
                CouponReceive couponReceive = new CouponReceive();
                couponReceive.setUserId(userId);
                couponReceive.setCouponId(model.getId());
                couponReceive.setReceiveTime(TimeUtil.timestamp());
                couponReceive.setAmount(model.getAmount());
                couponReceive.setStatus(0);
                couponReceive.setFullAmount(model.getFullAmount());
                Integer validType = model.getValidType();
                Integer validDays = model.getValidDays();
                if (validType != null && validType.intValue() == 2) {
                    couponReceive.setStartTime(curTimestamp);
                    couponReceive.setExpireTime(curTimestamp + validDays * 24 * 60 * 60);
                } else if (validType != null && validType.intValue() == 3) {
                    couponReceive.setStartTime(curTimestamp + 24 * 60 * 60);
                    couponReceive.setExpireTime(curTimestamp + 24 * 60 * 60 + validDays * 24 * 60 * 60);
                } else {
                    couponReceive.setStartTime(model.getValidStartTime());
                    couponReceive.setExpireTime(model.getValidEndTime());
                }
                this.couponReceiveMapper.insert(couponReceive);
                Integer dailyTakeCount = (Integer) RedisUtil.get("coupon_daily_take_count_" + format.format(new Date()));
                //每天已经领取的优惠券数量
                dailyTakeCount = dailyTakeCount != null ? dailyTakeCount : 0;
                dailyTakeCount = dailyTakeCount + 1;
                RedisUtil.set("coupon_daily_take_count_" + format.format(new Date()), dailyTakeCount);

                Integer takeCount = model.getTakeCount();
                takeCount = takeCount != null ? takeCount : 0;
                takeCount = takeCount + 1;
                model.setTakeCount(takeCount);
                this.couponMapper.updateById(model);
            }
        }
    }

    @Override
    public PageResult<CouponMineVo> mine(PageParam pageParam, Integer userId, int status) {
        Integer page  = pageParam.getPageNo();
        Integer limit = pageParam.getPageSize();
        MPJQueryWrapper<Coupon> mpjQueryWrapper = new MPJQueryWrapper<Coupon>()
                .selectAll(Coupon.class)
                .select("cr.expire_time AS validTime, cr.start_time AS startTime, cr.id AS receiveId ")
                .innerJoin("?_coupon_receive cr ON cr.coupon_id = t.id".replace("?_", GlobalConfig.knowledgePaymentTablePrefixe))
                .eq("t.status", 2)
                .eq("cr.user_id", userId)
                .isNull("t.delete_time")
                .orderByDesc(Arrays.asList("cr.receive_time"));
        //未使用
        if (status == 0) {
            mpjQueryWrapper.eq("cr.status", 0);
        } else if (status == 1) {
            mpjQueryWrapper.eq("cr.status", 1);
        } else if (status == 2) {
            mpjQueryWrapper.nested(wq->wq
                    .eq("cr.status", -1).or()
                    .eq("cr.status", 2).or());
        }
        IPage<CouponMineVo> iPage = couponMapper.selectJoinPage(new Page<>(page, limit), CouponMineVo.class, mpjQueryWrapper);
        iPage.getRecords().forEach(item -> {
            item.setValidTime(TimeUtil.timestampToDate(item.getStartTime(), "yyyy.MM.dd") + "~" + TimeUtil.timestampToDate(item.getValidTime(), "yyyy.MM.dd HH:mm"));
            item.setCourses(courseMapper.getCourseByCouponId(item.getId()));
        });
        return PageResult.iPageHandle(iPage.getTotal(), iPage.getCurrent(), iPage.getSize(), iPage.getRecords());
    }

    @Override
    public Map<String, Object> getMyCouponCount(Integer userId) {
        Map<String, Object> resultMap = new HashMap<>();
        resultMap.put("unused_count", couponReceiveMapper.countByStatusAndUserId(0, userId));
        resultMap.put("used_count", couponReceiveMapper.countByStatusAndUserId(1, userId));
        resultMap.put("expired_count", couponReceiveMapper.countByStatusAndUserId(-1, userId) + couponReceiveMapper.countByStatusAndUserId(2, userId));
        return resultMap;
    }

    @Override
    public List<CouponCenterVo> getCourseCoupon(Integer courseId, Integer userId) {
        List<CouponCenterVo> vos = new LinkedList<>();

        List<Coupon> all = new LinkedList<>();

        List<Coupon> normalCoupons = this.couponMapper.getAllAvailable();

        List<Coupon> courseCoupon = this.couponMapper.getCourseAvailable(courseId);

        List<Coupon> courseUnavailableCoupon = this.couponMapper.getCourseUnavailable(courseId);

        all.addAll(normalCoupons);
        all.addAll(courseCoupon);
        all.addAll(courseUnavailableCoupon);

        all.forEach(item-> {
            CouponCenterVo centerVo = new CouponCenterVo();
            centerVo.setReceiveStatus(checkCoupon(item, userId));

            Integer validType = item.getValidType();
            Integer validDays = item.getValidDays();
            String validTime = "";
            if (validType != null && validType.intValue() == 2) {
                validTime = "领券当日起" + validDays + "天内可用";
            } else if (validType != null && validType.intValue() == 3) {
                validTime = "领券次日起" + validDays + "天内可用";
            } else {
                validTime = TimeUtil.timestampToDate(item.getValidStartTime(),"yyyy.MM.dd HH:mm") + "~" + TimeUtil.timestampToDate(item.getValidEndTime(), "yyyy.MM.dd HH:mm");
            }
            centerVo.setId(item.getId());
            centerVo.setTitle(item.getTitle());
            centerVo.setAmount(item.getAmount());
            centerVo.setFullAmount(item.getFullAmount());
            centerVo.setValidTime(validTime);
            centerVo.setUsed(item.getUsed());
            centerVo.setType(item.getType());
            centerVo.setCourses(courseMapper.getCourseByCouponId(item.getId()));
            vos.add(centerVo);
        });
        return vos;
    }

    @Override
    public List<CouponMineVo> getMineAvailableCourseCoupon(Integer orderId, Integer userId, String amount, Long now) {
        List<CouponMineVo> vos = new LinkedList<>();
        Integer courseId = this.orderCourseMapper.getCouseId(orderId);
        List<ConponVo> normalCoupons = this.couponMapper.getMineAllAvailable(courseId, userId, amount, now);
        normalCoupons.forEach(item-> {
            CouponMineVo mineVo = new CouponMineVo();
            mineVo.setId(item.getId());
            mineVo.setTitle(item.getTitle());
            mineVo.setAmount(item.getAmount());
            mineVo.setFullAmount(item.getFullAmount());
            String startTime = TimeUtil.timestampToDate(item.getReceiveStartTime(), "yyyy.MM.dd");
            mineVo.setStartTime(startTime);
            mineVo.setValidTime(startTime + " ~ " + TimeUtil.timestampToDate(item.getExpireTime(), "yyyy.MM.dd HH:mm"));
            mineVo.setUsed(item.getUsed());
            mineVo.setType(item.getType());
            mineVo.setReceiveId(item.getReceiveId());
            mineVo.setCourses(courseMapper.getCourseByCouponId(item.getId()));
            vos.add(mineVo);
        });
        return vos;
    }

    @Override
    public List<CouponMineVo> getMineUnavailableCourseCoupon(Integer orderId, Integer userId, String amount, Long now) {

        List<CouponMineVo> availables = this.getMineAvailableCourseCoupon(orderId, userId, amount, now);
        List<Integer> ids = new ArrayList<>();
        availables.forEach(item -> {
            ids.add(item.getReceiveId());
        });

        MPJQueryWrapper<Coupon> mpjQueryWrapper = new MPJQueryWrapper<Coupon>()
                .selectAll(Coupon.class)
                .select("cr.expire_time AS validTime, cr.start_time AS startTime, cr.id AS receiveId ")
                .innerJoin("?_coupon_receive cr ON cr.coupon_id = t.id".replace("?_", GlobalConfig.knowledgePaymentTablePrefixe))
                .eq("t.status", 2)
                .eq("cr.user_id", userId)
                .isNull("t.delete_time")
                .eq("cr.status", 0);


        if (ids.size() > 0) {
            mpjQueryWrapper.notIn("cr.id", ids);
        }


        mpjQueryWrapper.orderByDesc(Arrays.asList("t.update_time"));

        List<CouponMineVo> mineVos = couponMapper.selectJoinList(CouponMineVo.class, mpjQueryWrapper);

        mineVos.forEach(item-> {
            item.setId(item.getId());
            item.setTitle(item.getTitle());
            item.setAmount(item.getAmount());
            item.setFullAmount(item.getFullAmount());
            String startTime = TimeUtil.timestampToDate(item.getStartTime(), "yyyy.MM.dd");
            item.setStartTime(startTime);
            item.setValidTime(startTime + " ~ " + TimeUtil.timestampToDate(item.getValidTime(), "yyyy.MM.dd HH:mm"));
            item.setUsed(item.getUsed());
            item.setType(item.getType());
            item.setReceiveId(item.getReceiveId());
            item.setCourses(courseMapper.getCourseByCouponId(item.getId()));
        });
        return mineVos;
    }

    @Override
    public void use(CouponUseParam couponUseParam) {
        if( couponUseParam != null) {
            CouponReceive model = couponReceiveMapper.selectOne(
                    new QueryWrapper<CouponReceive>()
                            .eq("id", couponUseParam.getReceiveId())
                            .last("limit 1"));

            Assert.isNull(model.getUsedTime(), "优惠券已被使用，不能重复使用");
            if (model != null) {
                model.setStatus(1);
                model.setUsedTime(TimeUtil.timestamp());
                model.setUsedOrderId(couponUseParam.getOrderId());
                this.couponReceiveMapper.updateById(model);
                Integer couponId = model.getCouponId();
                Coupon coupon = this.couponMapper.selectById(couponId);
                if (coupon != null) {
                    Integer usedCount = coupon.getUsedCount();
                    usedCount = usedCount != null ? usedCount : 0;
                    usedCount = usedCount + 1;
                    coupon.setUsedCount(usedCount);
                    this.couponMapper.updateById(coupon);
                }
            }
        }
    }
}
